package com.har01d.lang.compiler.visitor.statement;

import com.har01d.lang.antlr.Har01dBaseVisitor;
import com.har01d.lang.antlr.Har01dParser;
import com.har01d.lang.compiler.domain.Scope;
import com.har01d.lang.compiler.domain.function.FunctionReference;
import com.har01d.lang.compiler.domain.statement.expression.Expression;
import com.har01d.lang.compiler.domain.type.FunctionType;
import com.har01d.lang.compiler.domain.variable.Field;
import com.har01d.lang.compiler.domain.variable.FieldReference;
import com.har01d.lang.compiler.domain.variable.LocalVariable;
import com.har01d.lang.compiler.domain.variable.LocalVariableReference;
import com.har01d.lang.compiler.domain.variable.Reference;
import com.har01d.lang.compiler.exception.InvalidSyntaxException;
import com.har01d.lang.compiler.visitor.statement.expression.ExpressionVisitor;

public class VariableReferenceExpressionVisitor extends Har01dBaseVisitor<Reference> {

    private final ExpressionVisitor expressionVisitor;
    private final Scope scope;

    public VariableReferenceExpressionVisitor(ExpressionVisitor expressionVisitor, Scope scope) {
        this.expressionVisitor = expressionVisitor;
        this.scope = scope;
    }

    @Override
    public Reference visitVarReference(Har01dParser.VarReferenceContext ctx) {
        String name = ctx.variableReference().getText();

        if (ctx.owner != null) {
            Expression expression = ctx.owner.accept(expressionVisitor);
            if (scope.getClassName().equals(expression.getType().getName())) {
                Field field = scope.getField(name);
                if (field != null) {
                    if (!field.isInitialized()) {
                        throw new InvalidSyntaxException("Field '" + name + "' doesn't initialized!", ctx);
                    }

                    return new FieldReference(scope.getClassName(), field);
                } else {
                    throw new InvalidSyntaxException("Field '" + name + "' doesn't declared!", ctx);
                }
            }
        }

        LocalVariable variable = scope.getLocalVariable(name);
        if (variable == null) {
            variable = scope.getVariable(name);
            if (variable != null) {
                scope.addImplicitVariable(variable);
            }
        }

        if (variable == null) {
            FunctionReference functionReference = scope.getFunctionReference(name);
            if (functionReference != null) {
                return functionReference;
            }
        }

        if (variable == null) {
            Field field = scope.getField(name);
            if (field != null) {
                if (!field.isInitialized()) {
                    throw new InvalidSyntaxException("Field '" + name + "' doesn't initialized!", ctx);
                }

                return new FieldReference(scope.getClassName(), field);
            }
        }

        if (variable == null) {
            throw new InvalidSyntaxException("Variable '" + name + "' doesn't declared!", ctx);
        }

        if (!variable.isInitialized()) {
            throw new InvalidSyntaxException("Variable '" + name + "' doesn't initialized!", ctx);
        }

        if (variable.getType() instanceof FunctionType) {
            FunctionReference functionReference = scope.getFunctionReference(name);
            if (functionReference != null) {
                return functionReference;
            }
        }

        return new LocalVariableReference(variable);
    }

}
